// =============================================================================
#include <stdarg.h>
#include <string.h>

#include <util/common_macros.h>

#include "draw.h"
#include "color.h"

// =============================================================================

ImpDraw imp_draw_create(ImpImage * Img) {

	ImpDraw draw;

	draw.rgb = (Img->type == IMP_IMAGE_TYPE_RGB);
	
	draw.fg = imp_color(255,255,255);
	draw.bg = imp_color(0,0,0);
	
	draw.font = FONT_6x11;

	draw.alpha = 0;

	draw.img = Img;

	return draw;
}
void imp_draw_set_fg(ImpDraw * draw, Color color) {
	draw->fg = color;
}
void imp_draw_set_bg(ImpDraw * draw, Color color) {
	draw->bg = color;
}
// =============================================================================
#define imp_draw_point imp_draw_pixel
void imp_draw_pixel(ImpDraw * draw, int x, int y) {

	if (x < 0 || x >= draw->img->width) return;
	if (y < 0 || y >= draw->img->height) return;

	int idx = imp_image_idx(draw->img, x, y);

	unsigned char * data = draw->img->data;

	if (draw->rgb) {
		if (draw->alpha) {
			float a = draw->alpha;
			float c = 1 - a;
			data[idx + 0] = (data[idx + 0] * c) + (draw->fg.rgb.r * a);
			data[idx + 1] = (data[idx + 1] * c) + (draw->fg.rgb.g * a);
			data[idx + 2] = (data[idx + 2] * c) + (draw->fg.rgb.b * a);
		} else {
			data[idx] = draw->fg.rgb.r;
			data[idx + 1] = draw->fg.rgb.g;
			data[idx + 2] = draw->fg.rgb.b;
		}
	} else {
		// Gray
		if (draw->alpha) {
			float a = draw->alpha;
			float c = 1 - a;
			data[idx] = (data[idx] * c) + (draw->fg.px * a);
		} else {
			data[idx] = draw->fg.gray;
		}
	}


}
void imp_draw_segment(ImpDraw * draw, int x0, int y0, int x1, int y1) {

	// = draw vertical line
	if (x0 == x1) {
		if (y0 > y1) SWAP(y0, y1, int);
		for (; y0 <= y1; y0++) imp_draw_pixel(draw, x0, y0);
		return;
	}

	// = draw horizontal line
	if (y0 == y1) {
		if (x0 > x1) SWAP(x0, x1, int);
		for (; x0 <= x1; x0++) imp_draw_pixel(draw, x0, y0);
		return;
	}

	// = draw non vertical and non horizontal line
	int dx = abs(x1 - x0);
	int dy = -abs(y1 - y0);

	int sx = x0 < x1 ? 1 : -1;
	int sy = y0 < y1 ? 1 : -1;

	int err = dx + dy;
	int e2; // error value e_xy

	for (;;) { // loop 

		imp_draw_pixel(draw, x0, y0);

		if (x0 == x1 && y0 == y1) break;
		e2 = 2 * err;
		if (e2 >= dy) {
			err += dy;
			x0 += sx;
		} // e_xy+e_x > 0
		if (e2 <= dx) {
			err += dx;
			y0 += sy;
		} // e_xy+e_y < 0
	}
}
void imp_draw_rectangle(ImpDraw * draw, int x0, int y0, int x1, int y1) {

	/* horizontal */
	imp_draw_segment(draw, x0, y0, x1, y0);
	imp_draw_segment(draw, x0, y1, x1, y1);
	/* vertical */
	imp_draw_segment(draw, x0, y0, x0, y1);
	imp_draw_segment(draw, x1, y0, x1, y1);
}
void imp_draw_rectangle_fill(ImpDraw * draw, int x0, int y0, int x1, int y1) {

	for (; y0 <= y1; y0++) {
		imp_draw_segment(draw, x0, y0, x1, y0);
	}

}
void imp_draw_square(ImpDraw * draw, int x, int y, int ray) {
	int x1, x2, y1, y2;

	x1 = x - ray;
	x2 = x + ray;
	y1 = y - ray;
	y2 = y + ray;

	imp_draw_rectangle(draw, x1, y1, x2, y2);

	/*
		imp_draw_segment(draw, x1, y1, x2, y1);
		imp_draw_segment(draw, x1, y1, x1, y2);
		imp_draw_segment(draw, x2, y2, x2, y1);
		imp_draw_segment(draw, x2, y2, x1, y2);
	 */
}
void imp_draw_square_fill(ImpDraw * draw, int x, int y, int ray) {
	int x1, x2, y1, y2;

	x1 = x - ray;
	x2 = x + ray;
	y1 = y - ray;
	y2 = y + ray;

	imp_draw_rectangle_fill(draw, x1, y1, x2, y2);

	/*
		imp_draw_segment(draw, x1, y1, x2, y1);
		imp_draw_segment(draw, x1, y1, x1, y2);
		imp_draw_segment(draw, x2, y2, x2, y1);
		imp_draw_segment(draw, x2, y2, x1, y2);
	 */
}
void imp_draw_cross(ImpDraw * draw, int x, int y, int ray) {

	imp_draw_segment(draw, x - ray, y, x + ray, y);
	imp_draw_segment(draw, x, y - ray, x, y + ray);

}
// =============================================================================
// =============================================================================
void imp_draw_circle(ImpDraw * draw, int xm, int ym, int ray) {
	int x = -ray, y = 0, err = 2 - 2 * ray; /* II. Quadrant */
	do {
		imp_draw_pixel(draw, xm - x, ym + y); /*   I. Quadrant */
		imp_draw_pixel(draw, xm - y, ym - x); /*  II. Quadrant */
		imp_draw_pixel(draw, xm + x, ym - y); /* III. Quadrant */
		imp_draw_pixel(draw, xm + y, ym + x); /*  IV. Quadrant */

		ray = err;
		if (ray > x) err += ++x * 2 + 1; /* e_xy+e_x > 0 */
		if (ray <= y) err += ++y * 2 + 1; /* e_xy+e_y < 0 */
	} while (x < 0);
}


// The '(x != 0 && y != 0)' test in the last line of this function
// may be omitted for a performance benefit if the radius of the
// circle is known to be non-zero.
inline static void __plot4points(ImpDraw * draw, int cx, int cy, int x, int y) {
	imp_draw_pixel(draw, cx + x, cy + y);
	if (x != 0) imp_draw_pixel(draw, cx - x, cy + y);
	if (y != 0) imp_draw_pixel(draw, cx + x, cy - y);
	if (x != 0 && y != 0) imp_draw_pixel(draw, cx - x, cy - y);
}
inline static void __plot8points(ImpDraw * draw, int cx, int cy, int x, int y) {
	__plot4points(draw, cx, cy, x, y);
	if (x != y) __plot4points(draw, cx, cy, y, x);
}
void imp_draw_circle2(ImpDraw * draw, int xm, int ym, int ray) {

	int error = -ray;
	int x = ray;
	int y = 0;

	// The following while loop may altered to 'while (x > y)' for a
	// performance benefit, as long as a call to 'plot4points' follows
	// the body of the loop. This allows for the elimination of the
	// '(x != y') test in 'plot8points', providing a further benefit.
	//
	// For the sake of clarity, this is not shown here.
	while (x >= y) {

		__plot8points(draw, xm, ym, x, y);

		error += y;
		++y;
		error += y;

		// The following test may be implemented in assembly language in
		// most machines by testing the carry flag after adding 'y' to
		// the value of 'error' in the previous step, since 'error'
		// nominally has a negative value.
		if (error >= 0) {
			error -= x;
			--x;
			error -= x;
		}
	}

}
void imp_draw_circle3(ImpDraw * draw, int xm, int ym, int ray) {

	int f = 1 - ray;
	int ddF_x = 1;
	int ddF_y = -2 * ray;
	int x = 0;
	int y = ray;

	imp_draw_pixel(draw, xm, ym + ray);
	imp_draw_pixel(draw, xm, ym - ray);
	imp_draw_pixel(draw, xm + ray, ym);
	imp_draw_pixel(draw, xm - ray, ym);

	while (x < y) {
		// ddF_x == 2 * x + 1;
		// ddF_y == -2 * y;
		// f == x*x + y*y - radius*radius + 2*x - y + 1;
		if (f >= 0) {
			y--;
			ddF_y += 2;
			f += ddF_y;
		}
		x++;
		ddF_x += 2;
		f += ddF_x;
		imp_draw_pixel(draw, xm + x, ym + y);
		imp_draw_pixel(draw, xm - x, ym + y);
		imp_draw_pixel(draw, xm + x, ym - y);
		imp_draw_pixel(draw, xm - x, ym - y);
		imp_draw_pixel(draw, xm + y, ym + x);
		imp_draw_pixel(draw, xm - y, ym + x);
		imp_draw_pixel(draw, xm + y, ym - x);
		imp_draw_pixel(draw, xm - y, ym - x);
	}

}


// =============================================================================
static void __draw_text(ImpDraw * draw, int col, int row, char fill_ground, char * imp_image_label) {

	int i, x, y, f, len;
	int idx;
	int w, h;

	int width;
	int height;
	unsigned char * image;

	//Font font = FONT_6x11;
	Font font = draw->font;

	width = draw->img->width;
	height = draw->img->height;
	image = draw->img->data;

	len = strlen(imp_image_label);


	for (y = 0; y < font.char_height; y++) {
		h = row + y;
		if (h >= height || h < 0) continue;

		for (x = 0; x < len; x++) {

			f = font.fontdata[imp_image_label[x] * font.char_height + y];

			for (i = 0; i < font.char_width; i++) {

				w = col + (font.char_width * x) + i;

				if (w >= width || w < 0) continue;

				if (f & (font.mask >> i)) {

					imp_draw_pixel(draw, w, h);

				} else {
					if (fill_ground) {

						Color fg = draw->fg;
						draw->fg = draw->bg;
						imp_draw_pixel(draw, w, h);
						draw->fg = fg;

					}

				}
			}
		}
	}
}
void imp_draw_text(ImpDraw * draw, int x, int y, int fillbg, char * template, ...) {

	char msg[256];
	va_list ap;

	va_start(ap, template);
	vsprintf(msg, template, ap);
	va_end(ap);

	__draw_text(draw, x, y, fillbg, msg);
}

// =============================================================================
#include "../../geometry/geometry.h"
void imp_draw_geom_point(ImpDraw * draw, ImpPoint * p) {

	/*
		Color fg = draw->fg;
		//draw->fg = imp_color(255, 165, 0);
		draw->fg = imp_color(0,0,255);
		//draw->alpha = 0.3;
	 */
	imp_draw_circle(draw, p->x, p->y, 3);
	imp_draw_circle(draw, p->x, p->y, 4);
	/*
		draw->fg = fg;
		draw->alpha = 0;
	 */
}
void imp_draw_geom_segment(ImpDraw * draw, ImpSegment * s) {

	ImpPoint * p1;
	ImpPoint * p2;

	p1 = IMP_POINT(s->v[0]);
	p2 = IMP_POINT(s->v[1]);

	imp_draw_segment(draw, p1->x, p1->y, p2->x, p2->y);
}
void imp_draw_geom_triangle(ImpDraw * draw, ImpTriangle * t, boolean print_center) {

	imp_draw_geom_segment(draw, IMP_SEGMENT(t->e[0]));
	imp_draw_geom_segment(draw, IMP_SEGMENT(t->e[1]));
	imp_draw_geom_segment(draw, IMP_SEGMENT(t->e[2]));


	if (print_center) {
		ImpPoint p = imp_triangle_barycenter(t);
		imp_draw_square_fill(draw, p.x, p.y, 2);
	}
	//imp_draw_text(draw, p.x, p.y, FALSE, "%d", (int)t->id);

}
void imp_draw_list_of_geom_vertices(ImpDraw * draw, List * vertices) {

	ListIterator * it;
	ImpVertex * v;

	it = list_get_first(vertices);
	while (it) {

		imp_draw_geom_point(draw, IMP_POINT(it->data));
		v = IMP_VERTEX(it->data);
		//ImpPoint * p = IMP_POINT(v);

		//imp_draw_text(draw, p->x + 3, p->y + 3, FALSE, "%d", (int) v->id);

		//imp_imdraw_print_text(img, p->x+3, p->y+3, 0, "%d", (int) IMP_VERTEX(p)->id);

		it = it->next;
	}
}
void imp_draw_list_of_geom_edges(ImpDraw * draw, List * edges) {

	ListIterator * it;

	it = list_get_first(edges);
	while (it) {

		imp_draw_geom_segment(draw, IMP_SEGMENT(it->data));
		it = it->next;
	}
}
void imp_draw_list_of_geom_triangles(ImpDraw * draw, List * triangles, boolean print_center) {

	ListIterator * it;
	ImpTriangle * t;

	_return_if_fail(triangles);


	it = list_get_first(triangles);
	while (it) {
		t = IMP_TRIANGLE(it->data);

		imp_draw_geom_triangle(draw, t, print_center);

		it = it->next;
	}

}